//== SAGITTARIUS ============================================================================

//== BEGINNING OF CODE ===============================================================

#include "sagittarius/core/SagittariusCore.h"
#include "sagittarius/gmp/gmpsupport.h"
#include "sagittarius/io/io.h"
#include <malloc.h>
#include <string.h>

/*mpz_t* sg_alloc_bignum() {
    mpz_t* r=malloc(sizeof(mpz_t));
    AN(r);
    mpz_init_set_ui(*r, 0); //it isn't worth initializing & setting separately
    return r;
}

void sg_free_bignum(mpz_t* z) {
    AN(z, "sg_free_bignum: Expected valid pointer");
    mpz_clear(*z);
    free(z);
}*/

void sg_init_bignum(bignum_t* r) {
    mpz_init_set_ui(r->v, 0);
    r->flg = 1;
}

void sg_bignum_set(bignum_t* r, bignum_t* x) {
    if(!r->flg)
        sg_init_bignum(r);
    AN(x->flg);
    mpz_set(r->v, x->v);
}

void sg_bignum_set_ui(bignum_t* r, unsigned long x) {
    if(!r->flg)
        sg_init_bignum(r);
    mpz_set_ui(r->v, x);
}

bignum_t sg_make_bignum(unsigned long x) {
    bignum_t r;
    sg_bignum_set_ui(&r, x);
    return r;
}

void sg_bignum_clear(bignum_t* r) {
    if(r->flg)
        mpz_clear(r->v);
}

bignum_t* sg_bignum_create_buf(size_t len) {
    bignum_t* buf=malloc(len*sizeof(bignum_t));
    AN(buf, "malloc failure");
    memset(buf, 0, len*sizeof(bignum_t)); //only used for the side-effect of setting init_flag to zero for every element
    return buf;
}

void sg_bignum_clearbuf(bignum_t* buf, size_t len) {
    uint32 i;
    for(i=0; i<len; ++i) {
        mpz_clear(buf->v);
        buf->flg = 0;
        ++buf;
    }
}
void sg_bignum_deletebuf(bignum_t* buf, size_t len) {
    sg_bignum_clearbuf(buf, len);
    free(buf);
}

void gmpcopy(bignum_t* dest, bignum_t* src, size_t n) {
    uint32 i;
    for(i=0; i<n; ++i) {
        sg_bignum_set(dest, src);
        ++dest;
        ++src;
    }
}

/*void gmpmemoverwrite_ui(bignum_t* dest, unsigned long x, size_t n) {
    uint32 i;
    for(i=0; i<n; ++i) {
        //SG_WRITE(1, "loop\n");
        mpz_set_ui(*dest, x);
        ++dest;
    }
}*/

void gmpmemset_ui(bignum_t* dest, unsigned long x, size_t n) {
    uint32 i;
    for(i=0; i<n; ++i) {
        sg_bignum_set_ui(dest, x);
        ++dest;
    }
}

void sg_bignum_print(bignum_t a) {
    AN(a.flg);
    gmp_fprintf(stderr, "%Zd", a.v);
    fflush(stderr);
}

void sg_bignum_lshift(bignum_t* r, bignum_t a) {
    mpz_t tmp;
    mpz_init(tmp);
    mpz_set_ui(tmp, 2);
    mpz_pow_ui(tmp, tmp, mpz_get_ui(a.v));
    mpz_mul(r->v, r->v, tmp);
    mpz_clear(tmp);
}

void sg_bignum_lshift_ui(bignum_t* r, unsigned long x) {
    mpz_t tmp;
    mpz_init(tmp);
    mpz_set_ui(tmp, 2);
    mpz_pow_ui(tmp, tmp, x);
    mpz_mul(r->v, r->v, tmp);
    mpz_clear(tmp);
}

void sg_bignum_rshift(bignum_t* r, bignum_t a) {
    mpz_t tmp;
    mpz_init(tmp);
    mpz_set_ui(tmp, 2);
    mpz_pow_ui(tmp, tmp, mpz_get_ui(a.v));
    mpz_tdiv_q(r->v, r->v, tmp);
    mpz_clear(tmp);
}

void sg_bignum_rshift_ui(bignum_t* r, unsigned long x) {
    mpz_t tmp;
    mpz_init(tmp);
    mpz_set_ui(tmp, 2);
    mpz_pow_ui(tmp, tmp, x);
    mpz_tdiv_q(r->v, r->v, tmp);
    mpz_clear(tmp);
}
